/**
 * Created by ${PRODUCT_NAME}.
 * User: skylerparr
 * Date: 12/26/10
 * Time: 3:45 PM
 * To change this template use File | Settings | File Templates.
 */
package com.thoughtorigin.asset {
import com.thoughtorigin.loader.AbstractLoader;

import com.thoughtorigin.test.AsyncDispatcher;

import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;

import flash.events.EventDispatcher;

import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Responder;

import flash.utils.setTimeout;

import net.digitalprimates.fluint.async.AsyncHandler;

import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertTrue;
import org.flexunit.asserts.fail;
import org.flexunit.async.Async;
import org.mockito.api.Stubber;
import org.mockito.integrations.any;
import org.mockito.integrations.atLeast;
import org.mockito.integrations.between;
import org.mockito.integrations.callOriginal;
import org.mockito.integrations.given;
import org.mockito.integrations.inOrder;
import org.mockito.integrations.mock;
import org.mockito.integrations.never;
import org.mockito.integrations.notMoreThan;
import org.mockito.integrations.useArgument;
import org.mockito.integrations.verify;

[RunWith("org.mockito.integrations.flexunit4.MockitoClassRunner")]
[Mock(type="com.thoughtorigin.asset.AssetCache")]
[Mock(type="com.thoughtorigin.loader.AbstractLoader")]
public class CacheAssetManagerTest {

    private var _assetManager: TestableCacheAssetManager;

    [Before]
    public function setup(): void {
        _assetManager = new TestableCacheAssetManager();
    }

    [After]
    public function tearDown(): void {
        _assetManager = null;
    }

    [Test(async)]
    public function shouldLoadAnAsset(): void {
        var callback: Function = function(disp: DisplayObject): void {
            verify(between(1, 1)).that(loader.start());
            assertEquals(retVal, disp);

            var ad: AsyncDispatcher = new AsyncDispatcher();
            ad.addEventListener(Event.COMPLETE, async);
            ad.doDispatch();

        };

        var async: Function = Async.asyncHandler(this, function(e: Event, o: Object):void {
            assertTrue(true);
        },500)

        var retVal: Sprite = new Sprite();
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new Event(Event.COMPLETE)));
        given(loader.addEventListener(any(), any())).will(callOriginal());
        given(loader.content).willReturn(retVal);
        _assetManager.loadAsset(loader, callback);

    }

    [Test(async)]
    public function shouldOnlyLoadOneAssetAtATime(): void {
        var callback: Function = function(disp: DisplayObject): void {
            verify(notMoreThan(1)).that(loader.start());
            assertEquals(retVal, disp);

            var ad: AsyncDispatcher = new AsyncDispatcher();
            ad.addEventListener(Event.COMPLETE, async);
            ad.doDispatch();
        };

        var async: Function = Async.asyncHandler(this, function(e: Event, o: Object):void {
            assertTrue(true);
        },500)

        var retVal: Sprite = new Sprite();
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new Event(Event.COMPLETE)));
        given(loader.addEventListener(any(), any())).will(callOriginal());
        given(loader.content).willReturn(retVal);
        _assetManager.loadAsset(loader, callback);
        _assetManager.loadAsset(loader, callback);
        _assetManager.loadAsset(loader, callback);
    }

    [Test(async)]
    public function shouldLoadAssetOutOfTheCache(): void {
        var callback: Function = function(dispObject: DisplayObject): void {
            verify(between(1,1)).that(_assetCache.isAssetCached(any()));
            verify(between(1,1)).that(_assetCache.getAsset(any(), any()));
            verify(never()).that(loader.start());
            assertEquals(retVal, dispObject);

            var ad: AsyncDispatcher = new AsyncDispatcher();
            ad.addEventListener(Event.COMPLETE, async);
            ad.doDispatch();
        };

        var async: Function = Async.asyncHandler(this, function(e: Event, o: Object):void {
            assertTrue(true);
        },100)

        var _assetCache: AssetCache = mock(AssetCache);
        assertNotNull(_assetCache);
        _assetManager.assetCache = _assetCache;

        var retVal: Sprite = new Sprite();
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(_assetCache.isAssetCached(any())).willReturn(true);
        given(_assetCache.getAsset(any(), any())).will(useArgument(1).asFunctionAndCall(retVal));

        _assetManager.loadAsset(loader, callback);
    }

    [Test(async)]
    public function shouldCacheAsset(): void {
        var callback: Function = function(disp: DisplayObject): void {
            assertEquals(retVal, disp);
            verify(between(1,2)).that(_assetCache.isAssetCached(any()));
            verify(between(0,1)).that(_assetCache.cacheAsset(any()));

            var ad: AsyncDispatcher = new AsyncDispatcher();
            ad.addEventListener(Event.COMPLETE, async);
            ad.doDispatch();
        };

        var async: Function = Async.asyncHandler(this, function(e: Event, o: Object):void {
            assertTrue(true);
        },200)

        var _assetCache: AssetCache = mock(AssetCache);
        assertNotNull(_assetCache);
        _assetManager.assetCache = _assetCache;

        var retVal: Sprite = new Sprite();
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(_assetCache.isAssetCached(any())).willReturn(false);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new Event(Event.COMPLETE)));
        given(loader.addEventListener(any(), any())).will(callOriginal());

        given(_assetCache.getAsset(any(), any())).will(useArgument(1).asFunctionAndCall(retVal));

        _assetManager.loadAsset(loader, callback);
    }

    [Test(async)]
    public function shouldCallFailFunctionIfNetworkError(): void {
        var callback: Function = function(absLoader: AbstractLoader): void {
            var ad: AsyncDispatcher = new AsyncDispatcher();
            ad.addEventListener(Event.COMPLETE, async);
            ad.doDispatch();
        };

        var async: Function = Async.asyncHandler(this, function(e: Event, o: Object):void {
            assertTrue(true);
        },100)


        var retVal: Sprite = new Sprite();
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new IOErrorEvent(IOErrorEvent.NETWORK_ERROR)));
        given(loader.addEventListener(any(), any())).will(callOriginal());

        _assetManager.loadAsset(loader, null, callback);
    }

    [Test(async)]
    public function shouldCallFailFunctionIfIOError(): void {
        var callback: Function = function(absLoader: AbstractLoader): void {
            var ad: AsyncDispatcher = new AsyncDispatcher();
            ad.addEventListener(Event.COMPLETE, async);
            ad.doDispatch();
        };

        var async: Function = Async.asyncHandler(this, function(e: Event, o: Object):void {
            assertTrue(true);
        },100)


        var retVal: Sprite = new Sprite();
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new IOErrorEvent(IOErrorEvent.IO_ERROR)));
        given(loader.addEventListener(any(), any())).will(callOriginal());

        _assetManager.loadAsset(loader, null, callback);
    }

    [Test(async)]
    public function shouldCallFailFunctionIfSecurityError(): void {
        var callback: Function = function(absLoader: AbstractLoader): void {
            var ad: AsyncDispatcher = new AsyncDispatcher();
            ad.addEventListener(Event.COMPLETE, async);
            ad.doDispatch();
        };

        var async: Function = Async.asyncHandler(this, function(e: Event, o: Object):void {
            assertTrue(true);
        },500)


        var retVal: Sprite = new Sprite();
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new SecurityErrorEvent(SecurityErrorEvent.SECURITY_ERROR)));
        given(loader.addEventListener(any(), any())).will(callOriginal());

        _assetManager.loadAsset(loader, null, callback);
    }

    [Test]
    public function shouldNotThrowExceptionIfErrorAndNoCallbackDefined(): void {
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new SecurityErrorEvent(SecurityErrorEvent.SECURITY_ERROR)));
        given(loader.addEventListener(any(), any())).will(callOriginal());

        try {
            _assetManager.loadAsset(loader, null);
        } catch(e: TypeError) {
            fail();
        }


    }

    [Test]
    public function shouldNotThrowExceptionIfSuccessAndNoCallbackDefined(): void {
        var loader: AbstractLoader = mock(AbstractLoader, "", ["url"]);
        given(loader.dispatchEvent(any())).will(callOriginal());
        given(loader.start()).will(new EventAnswer(loader, new Event(Event.COMPLETE)));
        given(loader.addEventListener(any(), any())).will(callOriginal());

        try {
            _assetManager.loadAsset(loader, null);
        } catch(e: TypeError) {
            fail();
        }
    }

}
}

import com.thoughtorigin.asset.CacheAssetManager;
import com.thoughtorigin.loader.AbstractLoader;

import flash.events.Event;

import flash.utils.setTimeout;

import org.mockito.api.Answer;

class EventAnswer implements Answer {

    private var _loader: AbstractLoader;
    private var _eventToDispatch: Event;

    public function EventAnswer(loader: AbstractLoader, eventToDispatch: Event) {
        _loader = loader;
        _eventToDispatch = eventToDispatch;
    }

    public function give():* {
        setTimeout(function(): void {
            _loader.dispatchEvent(_eventToDispatch);
        }, 0);
    }
}

class TestableCacheAssetManager extends CacheAssetManager {

    override protected function addToLoadingList(loader:AbstractLoader, successFunc:Function, failFunc:Function = null):void {

    }
}

